import asyncio
import json
import logging
from datetime import datetime

from PIL import Image, ImageDraw, ImageFont

from src64.models.body_analysis_model import BodyAnalysisModel
from src64.models.save_report_model import SaveReportModel, PatientCheckItemList, PatientCheckSignWordList, \
    PatientCheckSignWordDetailList
from src64.post_data import PostData


class BodyAnalysisDrawReport:
    """
    因为没有主动触发事件,所以这里启动定时器查询报告
    绘制人体成分分析报告图片
    """
    cur_test_id = None  # 记录当前查询到的报告id,防止数据重复提交
    report_model: BodyAnalysisModel  # 报告数据模型

    body_type_dict = {
        '1': '隐形肥胖型',
        '2': '肌肉不足型',
        '3': '消瘦型',
        '4': '脂肪过多型',
        '5': '健康匀称型',
        '6': '低脂肪型',
        '7': '肥胖型',
        '8': '超重肌肉型',
        '9': '运动员型'}

    def __init__(self, cfg, cursor, user, conn):
        """
        :param cfg: conf.ini 配置文件内容
        :param cursor: access数据库查询
        :param user: 医生登录信息
        """
        self.cfg = cfg
        self.user_info = user
        self.upload = PostData(cfg, user)
        if cfg.has_option(section='access', option='pre_table'):
            self.pre_table = cfg['access']['pre_table']
        self.cursor = cursor
        self.conn = conn
        self.logger = logging.getLogger('main')
        if not cfg.has_option(section='sqlite', option='database'):  # 如果是新系统,这里不启动定时任务
            with open('body_analysis_2a.json', 'r', encoding='utf-8') as f:
                self.conf = f.read()
                self.conf = json.loads(self.conf)
            if self.conf:
                self.img_path = self.conf['img_path']
                self.save_path = self.conf['save_path']
            self.start()
        else:
            self.pre_table = cfg['sqlite']['pre_table']

    async def timer(self, interval):
        while True:
            await self.select_report()
            await asyncio.sleep(interval)

    async def start_timer_loop(self):
        task = asyncio.gather(self.timer(30))  # 设置定时器间隔为30秒

    def start(self):
        """
        开启定时器
        :return:
        """
        asyncio.get_event_loop().run_until_complete(self.start_timer_loop())

    async def select_report(self):
        """
        查询用户报告信息,因为用户信息表可能存在未做的患者,这里从报告表开始查询
        :return:
        """
        try:
            self.cursor.execute('select top 1 * from TestData order by TestID desc')
            r = self.cursor.fetchone()
            self.logger.info('TestData查询:{}'.format(r))
            if r and self.cur_test_id != r[0]:
                # 如果当前查询的报告结果与当前记录的id不一致,说明是新的报告,则向下进行处理
                if r[-1].date() == datetime.now().date():
                    # 如果报告为当前的,则进行后面的处理
                    test_no = r[1]  # 记录受检者的检查编号
                    report_model = BodyAnalysisModel()
                    report_model.set_test_info(r)
                    self.cursor.execute(f'select * from ConnerInfo where ID={test_no}')
                    info = self.cursor.fetchone()
                    self.logger.info('ConnerInfo查询:{}'.format(info))
                    if info:  # 用户基本信息
                        report_model.set_info(info)

                    self.cursor.execute(f'select * from TestZuKang where TestNo={test_no}')
                    imp = self.cursor.fetchone()
                    self.logger.info('TestZuKang查询:{}'.format(imp))
                    if imp:  # 报告阻抗信息
                        report_model.set_impedance(imp)

                    self.cursor.execute(f'select * from TestJieDuan where TestNo={test_no}')
                    jieD = self.cursor.fetchone()
                    self.logger.info('TestJieDuan查询:{}'.format(jieD))
                    if jieD:  # 报告节段分析
                        report_model.set_jieduan(jieD)

                    self.cursor.execute(f'select * from TestDataScale where TestNo={test_no}')
                    scale = self.cursor.fetchone()
                    self.logger.info('TestDataScale:{}'.format(scale))
                    if scale:  # 报告节段分析
                        report_model.set_scale(scale, self.conf)

                    await self.draw_report(report_model, info[-1])
                    self.cur_test_id = r[0]

        except Exception as e:
            self.logger.error('查询出错:{}'.format(e))

    async def insert_body_analysis_info(self, data, regNo):
        """
        将患者信息插入到人体成分分析数据库中
        :param data:
        :param regNo:
        :return:
        """
        if self.cfg.has_option(section='sqlite', option='database'):
            # 新系统使用sqlite数据库
            self.cursor.execute(f"select * from {self.pre_table} where UID='{regNo}'")
            r = self.cursor.fetchone()
            self.logger.info("查询结果:{}".format(r))
            if not r:
                birthday = data['birthday']
                if birthday:
                    birthday = birthday.split('-')
                try:
                    self.cursor.execute(
                        f"insert into {self.pre_table} ([UID],[UUID],[Name],[BirthYear],[BirthMonth],[BirthDay],[Sex])"
                        f"values ({regNo},'PEIS','{data['patientName']}','{birthday[0]}','{birthday[1]}','{birthday[2]}','{'男' if data['sex'] == 1 else '女'}')")
                    r = self.conn.commit()
                    self.logger.info("commit:{}".format(r))
                except Exception as e:
                    self.logger.info('插入数据出错:{}'.format(e))
            else:
                self.logger.info("没有查询到最后一条数据")
        else:
            self.cursor.execute(f"select * from {self.pre_table} where UserCode='{regNo}'")
            r = self.cursor.fetchone()
            self.logger.info("查询结果:{}".format(r))
            if not r:
                self.cursor.execute(f'select top 1 * from {self.pre_table} order by ID desc')
                top = self.cursor.fetchone()
                if top:
                    ID = top[0] + 1
                    birthday = data['birthday']
                    if birthday:
                        birthday = birthday[:4]
                    try:
                        self.cursor.execute(
                            f"insert into {self.pre_table} ([ID],[Name],[BirthdayYear],[Sex],[UserCode])"
                            f"values ({ID},'{data['patientName']}','{birthday}','{'男' if data['sex'] == 1 else '女'}','{regNo}')")
                        self.cursor.commit()
                    except Exception as e:
                        self.logger.info('插入数据出错:{}'.format(e))
                else:
                    self.logger.info("没有查询到最后一条数据:{}".format(top))

    def get_tuple_by_key(self, section, key):
        """
        根据配置文件获取绘制坐标点
        :param section:
        :param key:
        :return: 如果是奇数,说明配置的有字体大小,如果是偶数说明没有,使用默认
        """
        t = tuple(self.conf['pos'][section][key])
        if len(t) % 2 == 0:
            return t, None
        else:
            return t[:-1], t[-1]

    def get_value_by_key(self, data, key):
        """
        根据key值获取model中设置的数值,进行格式化
        :param data:
        :param key:
        :return:
        """
        return str(data.__getattribute__(key)).strip()

    async def draw_report(self, data: BodyAnalysisModel, no):
        """
        绘制报告信息
        :param no: 体检号
        :param save_dir: 保存报告文件夹路径
        :param data: 查询到的患者报告数据
        :return:
        """
        image = Image.open(self.img_path)

        # 创建一个可以在给定图像上绘制的对象
        draw = ImageDraw.Draw(image)

        # 设置字体和大小
        font = ImageFont.truetype(self.conf['font'], self.conf['font_size'], encoding=self.conf['encoding'])
        text_color = (0, 0, 0)
        text_list = self.conf['pos']['text'].keys()
        selector_list = self.conf['pos']['selector'].keys()
        lines = self.conf['pos']['lines'].keys()
        setattr(data, 'hos_name', self.conf['hos_name'])
        setattr(data, 'phone', f'Tel:{self.conf["phone"]}')
        for t in text_list:
            if hasattr(data, t):
                p, s = self.get_tuple_by_key('text', t)
                draw.text(p, self.get_value_by_key(data, t), fill=text_color, font=(
                    ImageFont.truetype(self.conf['font'], s, encoding=self.conf['encoding']) if s else font))
        for t in selector_list:
            # 选择框的绘制包括两种情况,一种是√,一种是方块儿
            if hasattr(data, t):
                pos = self.get_tuple_by_key('selector', t)[0]
                if len(pos) == 2:
                    # 如果位置坐标是两位的,则说明是√
                    draw.text(pos, self.get_value_by_key(data, t), fill=text_color, font=font)
                elif len(pos) == 4:
                    # 如果坐标是四位的,则说明是方块儿
                    draw.rectangle(pos, (0, 0, 0))
        for t in lines:
            if hasattr(data, t):
                pos = self.get_tuple_by_key('lines', t)[0]
                if len(pos) == 2:  # 如果是二维数组,说明只有线的起点,线的终点从model中获取
                    # 线的坐标格式为(x,y)
                    draw.line((pos, data.__getattribute__(t)), fill=text_color,
                              width=(self.conf['line_height'] if self.conf['line_height'] else 3))
                elif len(pos) == 4:  # 如果是四维数组,则说明指定了开始结束的坐标点,格式为[x,y,x1,y1]
                    draw.line(((pos[0], pos[1]), (pos[2], pos[3])), fill=text_color,
                              width=(self.conf['line_height'] if self.conf['line_height'] else 3))

        # 保存图片
        img_path = f"{self.save_path}\\{no}.jpg"
        image.save(img_path)
        await self.post_report(data, img_path)
        r = await PostData(self.cfg, self.user_info).upload_file(
            f"{self.save_path}\\{no}.jpg")

    async def get_report_by_path(self, path):
        """
        根据报告的名称获取报告对应的结果,将结果上传到服务器
        :param path:
        :return:
        """
        try:
            UID = path.split('\\')[-1].split('.')[0]
            if self.cursor:
                self.cursor.execute(
                    f"SELECT BodyType,sex FROM Result WHERE UID = '{UID}'")
                r = self.cursor.fetchone()
                self.logger.info("查询结果:{},查询ID:{}".format(r, UID))
                if r:
                    body_type = r[0]
                    data = BodyAnalysisModel()
                    data.result = self.body_type_dict[body_type]
                    data.sex = r[1]
                    data.reg_no = UID
                    await self.post_report(data, path)
        except Exception as e:
            self.logger.error("查询报告结果出错:{}".format(e))

    async def post_report(self, data, img_path):
        """
        上传碳人体成分报告信息到服务器
        :param img_path: 报告图片的地址
        :param data:
        :return:
        """
        result = data.result
        if data and self.upload:
            img_url = None
            if img_path:
                img_res = await self.upload.upload_file(img_path)
                logging.getLogger('main').info('上传图片:{}'.format(img_res))
                if img_res.code == 0:
                    img_url = img_res.resultMap.url
            check_sign_word = await self.upload.list_check_sign_word(data.reg_no, 1 if data.sex == '男' else 2)
            if check_sign_word.code == 0 and check_sign_word.data:
                check_sign_info = check_sign_word.data[0]
                sign_word_list = check_sign_info.signWordList
                if sign_word_list:
                    sign_word_info = sign_word_list[0]
                    sign_word_detail_list = [s for s in sign_word_info.signWordDetailList if result == s.signWordDetail]
                    if sign_word_detail_list:
                        sign_word_detail_info = sign_word_detail_list[0]
                        save_model = SaveReportModel()
                        save_model.deptId = check_sign_info.deptId
                        save_model.deptName = check_sign_info.deptName
                        save_model.registNo = check_sign_info.registNo
                        save_model.briefSummary = f'{sign_word_info.signWord}:{sign_word_detail_info.signWordDetail}'
                        save_model.isLisSummary = 0
                        save_model.isDelaySummary = 0
                        patientCheckItemList = PatientCheckItemList()
                        patientCheckItemList.patientCheckId = check_sign_info.patientCheckId
                        patientCheckItemList.itemId = check_sign_info.itemId
                        patientCheckItemList.itemName = check_sign_info.itemName
                        patientCheckItemList.itemType = check_sign_info.itemType
                        patientCheckSignWordList = PatientCheckSignWordList()
                        patientCheckSignWordList.signWord = sign_word_info.signWord
                        patientCheckSignWordList.signWordId = sign_word_info.signWordId
                        patientCheckSignWordList.signWordSummary = sign_word_detail_info.signWordDetail
                        patientCheckSignWordDetailList = PatientCheckSignWordDetailList()
                        patientCheckSignWordDetailList.signWordDetail = sign_word_detail_info.signWordDetail
                        patientCheckSignWordDetailList.signWordDetailId = sign_word_detail_info.signWordDetailId
                        patientCheckSignWordDetailList.conclusionWord = sign_word_detail_info.conclusionWord
                        patientCheckSignWordDetailList.conclusionWordId = sign_word_detail_info.conclusionWordId
                        patientCheckSignWordDetailList.severeDegree = sign_word_detail_info.severeDegree
                        patientCheckSignWordDetailList.signWordDetailSummary = result
                        patientCheckSignWordList.patientCheckSignWordDetailList = [
                            patientCheckSignWordDetailList.__dict__]
                        patientCheckItemList.patientCheckSignWordList = [patientCheckSignWordList.__dict__]
                        patientCheckItemList.patientCheckPictureList = [img_url] if img_url else []
                        save_model.patientCheckItemList = [patientCheckItemList.__dict__]

                        save_res = await self.upload.save_check_report(save_model.__dict__)
                        logging.getLogger('main').info('上传结果:{}'.format(save_res))
                        if save_res.code == 0:
                            await self.upload.complete_check(check_sign_info.registNo)
